/**
 * This file is part of aion-core <aioncore.com>
 *
 *  aion-core is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  aion-core is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with aion-core.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionemu.loginserver.controller;

import java.sql.Timestamp;
import java.util.Iterator;
import java.util.Set;
import java.util.Map;
import javolution.util.FastMap;

import org.apache.log4j.Logger;

import com.aionemu.commons.database.dao.DAOManager;
import com.aionemu.commons.utils.NetworkUtils;
import com.aionemu.loginserver.configs.Config;
import com.aionemu.loginserver.dao.BannedIpDAO;
import com.aionemu.loginserver.model.BannedIP;
import com.aionemu.loginserver.model.FailedLoginAttempt;

/**
 * Class that controlls all ip banning activity
 *
 * @author SoulKeeper
 */
public class BannedIpController {
	/**
	 * Logger for this class.
	 */
	private static final Logger log = Logger.getLogger(BannedIpController.class);

	/**
	 * List of banned ip adresses
	 */
	private static Set<BannedIP> banList;

	private static Map<String, FailedLoginAttempt>    failedToLoginAddresses  = new FastMap<String, FailedLoginAttempt>();

	/**
	 * Loads list of banned ips
	 */
	public static void load() {
		reload();
	}

	/**
	 * Loads list of banned ips
	 */
	public static void reload() {
		// we are not going to make ip ban every minute, so it's ok to simplify a concurrent code a bit
		banList = getDAO().getAllBans();
		log.info("BannedIpController loaded " + banList.size() + " IP bans.");
	}

	/**
	 * Checks if ip (or mask) is banned
	 *
	 * @param ip ip address to check for ban
	 * @return is it banned or not
	 */
	public static boolean isBanned(String ip) {
		for (BannedIP ipBan : banList) {
			if (ipBan.isActive() && NetworkUtils.checkIPMatching(ipBan.getMask(), ip))
				return true;
		}
		return false;
	}

	/**
	 * Bans ip or mask for infinite period of time
	 *
	 * @param ip ip to ban
	 * @return was ip banned or not
	 */
	public static boolean banIp(String ip) {
		return addOrUpdateBan(ip, null);
	}

	/**
	 * Adds or updates ip ban. Changes are reflected in DB
	 * 
	 * @param ip
	 *            ip to ban
	 * @param expireTime
	 *            ban expiration time, null = never expires
	 * @return was ip banned or not
	 */
	public static boolean addOrUpdateBan(String ip, Timestamp expireTime)
	{
		BannedIP ipBan = new BannedIP();
		ipBan.setMask(ip);
		ipBan.setTimeEnd(expireTime);
		Iterator<BannedIP> it = banList.iterator();
		while (it.hasNext())
		{
			BannedIP banListItem = it.next();
			if (ipBan.getMask().equals(banListItem.getMask()))
			{
				banListItem.setTimeEnd(expireTime);
				getDAO().update(banListItem);
				return true;
			}
		}
		if(getDAO().insert(ipBan))
		{
			banList.add(ipBan);
			return true;
		}
		return false;
	}

	/**
	 * Removes ip ban.
	 *
	 * @param ip ip to unban
	 * @return returns true if ip was successfully unbanned
	 */
	public static boolean unbanIp(String ip) {
		Iterator<BannedIP> it = banList.iterator();
		while (it.hasNext()) {
			BannedIP ipBan = it.next();
			if (ipBan.getMask().equals(ip)) {
				if (getDAO().remove(ipBan)) {
					it.remove();
					return true;
				} else
					break;
			}
		}
		return false;
	}

	/**
	 * Retuns {@link com.aionemu.loginserver.dao.BannedIpDAO} , just a shortcut
	 *
	 * @return {@link com.aionemu.loginserver.dao.BannedIpDAO}
	 */
	private static BannedIpDAO getDAO() {
		return DAOManager.getDAO(BannedIpDAO.class);
	}

	/**
	 * @param address
	 */
	public static void handleGoodLogin(String address)
	{
		if(address != null)
			failedToLoginAddresses.remove(address);
	}

	public static void handleBadLogin(String password, String address)
	{
		if (address != null) {
			FailedLoginAttempt failedAttempt = failedToLoginAddresses.get(address);
			int failedCount;
			if (failedAttempt == null) {
				failedToLoginAddresses.put(address, new FailedLoginAttempt(address, password));
				failedCount = 1;
			}
			else {
				failedAttempt.increaseCounter(password);
				failedCount = failedAttempt.getCount();
			}
			if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN) {
				addOrUpdateBan(address.toString(), new Timestamp(System.currentTimeMillis() + (Config.WRONG_LOGIN_BAN_TIME * 60 * 1000)));
				reload();
			}
		}
	}
}
